﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Diagnostics;
using nRoute.Components;
using nRoute.Components.Composition;
using nRoute.Mapping;
using nRoute.Samples.Netflix.Data;
using nRoute.Samples.Netflix.Data.Catalog;
using nRoute.Samples.Netflix.Views;
using nRoute.ViewModels;

namespace nRoute.Samples.Netflix.ViewModels
{
    [DefineNavigationContent("Content/Netflix/Genres/", typeof(GenresListingView))]
    [MapViewModel(typeof(GenresListingView))]
    public class GenresListingViewModel 
        : NavigationViewModelBase, ISupportSearching
    {
        private const string GENRES_KEY = "GenresKey";
        private const string SEARCH_KEY = "SearchKey";

        private NetflixDataContext _context;
        private DataServiceCollection<Genre> _genres;
        private ValueConverter<Genre, DataServiceQuery<Title>> _converter;

        [ResolveConstructor]
        public GenresListingViewModel(NetflixDataContext context)
        {
            _context = context;
            SetupCommands();
        }

#region Properties

        public IEnumerable<Genre> Genres 
        {
            get 
            {
                if (_genres == null) return null;

                // note search here is basically just a filter, as we pre-load all the genres on the first go..
                return (SearchText == null) ? _genres : _genres.Where((g) => g.Name.IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1); 
            }
        }

        public bool IsLoading { get; private set; }

        public string SearchText { get; private set; }

        public ValueConverter<Genre, DataServiceQuery<Title>> GenreToTitleQueryConverter
        {
            get 
            {
                if (_converter == null) _converter = CreateToTitleConverter();
                return _converter;
            }
        }

        public ActionCommand ClearSearchCommand { get; private set; }

        public ActionCommand LoadDataCommand { get; private set; }

#endregion

#region ISearachable Implementation

        public void Search(string searchText)
        {
            if (string.IsNullOrWhiteSpace(searchText)) throw new ArgumentNullException("searchText");
            if (this.IsLoading) return;

            SearchText = searchText;
            NotifyPropertyChanged(() => SearchText);
            NotifyPropertyChanged(() => IsSearching);
            NotifyPropertyChanged(() => Genres);
        }

        public bool IsSearching
        {
            get { return (!string.IsNullOrWhiteSpace(this.SearchText)); }
        }

        public void CancelSearch()
        {
            SearchText = null;
            NotifyPropertyChanged(() => SearchText);
            NotifyPropertyChanged(() => IsSearching);
            NotifyPropertyChanged(() => Genres);
        }

#endregion

#region Overrides

        protected override void OnRestoreState(ParametersCollection state)
        {
            if (state != null && state.ContainsKey(GENRES_KEY))
            {
                _genres = (DataServiceCollection<Genre>)state[GENRES_KEY];
                NotifyPropertyChanged(() => Genres);     

                this.SearchText = Convert.ToString(state[SEARCH_KEY]);
                if (!string.IsNullOrWhiteSpace(this.SearchText)) NotifyPropertyChanged(() => Genres);
                NotifyPropertyChanged(() => SearchText);
                NotifyPropertyChanged(() => IsSearching);

                if (_genres.Count == 0) LoadGenres();
            }
        }

        protected override ParametersCollection OnSaveState()
        {
            if (_genres != null)
            {
                return new ParametersCollection()  
                {
                    new Parameter(GENRES_KEY, _genres),
                    new Parameter(SEARCH_KEY, this.SearchText)
                };
            }
            else
            {
                return null;
            }
        }

#endregion
        
#region Helpers

        private void SetupCommands()
        {
            LoadDataCommand = new ActionCommand(() =>
            {
                if (this.Genres == null && !this.IsLoading) LoadGenres();
            });

            ClearSearchCommand = new ActionCommand(CancelSearch);
        }

        private void LoadGenres()
        {
            if (_genres == null) _genres = new DataServiceCollection<Genre>();            

            IsLoading = true;
            NotifyPropertyChanged(() => IsLoading);

            // holds the result
            IAsyncResult _result = null;
            
            try
            {
                // we load everything at once
                var _query = (DataServiceQuery<Genre>)(from g in _context.Genres
                                                       orderby g.Name
                                                       select g).OrderBy((g) => g.Name);

                // execute async 
                _result = _query.BeginExecute((i) =>
                {
                    _genres.Load(_query.EndExecute(_result));
                    NotifyPropertyChanged(() => Genres);    

                    IsLoading = false;
                    NotifyPropertyChanged(() => IsLoading);       
                }, null);
            }
            catch
            {
                // todo: handle properly
                Debugger.Break();
                if (_result != null && !_result.IsCompleted) _context.CancelRequest(_result);

                // reset
                _genres = null;
                NotifyPropertyChanged(() => Genres);
                LoadGenres();
            }
        }

        private ValueConverter<Genre, DataServiceQuery<Title>> CreateToTitleConverter()
        {
            return new ValueConverter<Genre, DataServiceQuery<Title>>((g) => (DataServiceQuery<Title>)(
                    from _g in _context.Genres.IncludeTotalCount()
                    from _t in _g.Titles
                    where _g.Name == g.Name
                    select _t));
        }

#endregion

    }
}